/**
 * \file Файл описания членов класса Plot2D
 *
 * Файл содержит определение членов класса для отрисовкиб масштабирования сдвига графиков на холсте
 */
#include "plot2d.h"

using namespace boost;

Plot2D::Plot2D ()

     {
        m_maxSize = 0;
        m_delta = 0;
        auto GestureClick = Gtk::GestureClick::create();
        auto MotionController = Gtk::EventControllerMotion::create();
        auto ScrollController = Gtk::EventControllerScroll::create();
        GestureClick->signal_pressed().connect(sigc::mem_fun(*this, &Plot2D::MouseButtonPressed));
        GestureClick->signal_released().connect(sigc::mem_fun(*this, &Plot2D::MouseButtonReleased));
        MotionController->signal_motion ().connect(sigc::mem_fun(*this, &Plot2D::Motioning));
        ScrollController->signal_scroll().connect(sigc::mem_fun(*this, &Plot2D::Scrolling), false);
        ScrollController->set_flags(Gtk::EventControllerScroll::Flags::VERTICAL);
        set_draw_func(sigc::mem_fun(*this, &Plot2D::OnDraw));
        add_controller(GestureClick);
        add_controller(MotionController);
        add_controller(ScrollController);
}

bool Plot2D::Scrolling(double dx, double dy)
{
    double xMin;
    double xMax;
    double deltaX;
    xMin = m_interval_begin;
    xMax = m_interval_end;

    if (xMax == 0) xMax = m_maxSize;

    deltaX = (xMax-xMin) * XY_SCROLL_COEF;
    if (dy == 1) {
            xMin = xMin-deltaX;
            xMax = xMax+deltaX;
                if ((xMax - xMin) > m_maxSize * 0.95) return true;    //если очень большая дельта то ничего не увеличиваем
                    if ((xMax >= m_maxSize) | (xMax <= 5)) xMax = 0;
                        if (xMin <= 20)  xMin = 20;
                        m_delta = m_delta + XY_SCROLL_COEF;
                   //     PlotDraw(m_current_axes_name, xMin, xMax, m_Y_offset_min - m_delta, m_Y_offset_max + m_delta);
        PlotDraw(m_current_axes_name, xMin, xMax, 0, m_Y_offset_max + m_delta);
        }

    else
        if (dy == -1) {
            xMin=xMin + deltaX;
            xMax=xMax - deltaX;
                if ((xMax - xMin) < 30) return true;                //если очень маленькая дельта то ничего не уменьшаем
                    if ((xMax >= m_maxSize) | (xMax <= 5)) xMax = 0;
                        if (xMin <= 20)  xMin = 20;
                        m_delta = m_delta - XY_SCROLL_COEF;
                  //      PlotDraw(m_current_axes_name, xMin, xMax, m_Y_offset_min + m_delta, m_Y_offset_max - m_delta);
            PlotDraw(m_current_axes_name, xMin, xMax, 0, m_Y_offset_max - m_delta);
    }

    return true;
}

void Plot2D::MouseButtonPressed(int n_press, double x, double y)
{

    m_StartPosX = x;
    m_StartPosY = y;
    int width = get_allocated_width ();
    if ((n_press == 2) && (x < width * X_OFFSET_LEFT)) {    //изменение имени плота для отображения значений осей на графике
            auto i = m_plot_data.begin();
                while (i != m_plot_data.end()) {
                        m_PlotData2D_temp = *i;
                            if (m_current_axes_name == m_PlotData2D_temp->GetName()) {
                                ++i;
                                    if (i==m_plot_data.end()) {
                                        i = m_plot_data.begin();
                                        m_PlotData2D_temp = *i;
                            } else {
                                    m_PlotData2D_temp = *i;
                                    }
                m_current_axes_name = m_PlotData2D_temp->GetName();
                m_Y_offset_min = m_PlotData2D_temp->GetMinOffsetY();
                m_Y_offset_max = m_PlotData2D_temp->GetMaxOffsetY();
                break;
                }
        ++i;
        }
queue_draw();//полная перерисовка области
    }

    if ((n_press == 2) && (x > width * X_OFFSET_LEFT)) {  // возвращение графика к исходному масштабированию
        m_Y_offset_min = 999;
        m_Y_offset_max = 999;
        m_interval_begin = 300; // количество интервалов для отображения по умолчанию от конца массива данных(изменить !!!!!!)
        m_interval_end = 0;
        queue_draw();//полная перерисовка области
        m_Y_offset_min = 0.001;
        m_Y_offset_max = 0.001;
    }
    m_MotionOn = true;
}

void Plot2D::MouseButtonReleased(int n_press, double x, double y)
{
     m_MotionOn = false;
}

void Plot2D::Motioning(double x, double y)
{
    double deltaX = 0.0;
    double deltaY = 0.0;
    int width, height;
    width = get_allocated_width ();
    height = get_allocated_height ();
   if ((m_MotionOn == true) && (m_StartPosX > X_OFFSET_LEFT*width)) {   //сдвиг графиков по оси Х
    deltaX = X_SHIFT_COEF * (x - m_StartPosX) / (static_cast<double> (width) * (1 - X_OFFSET_LEFT - X_OFFSET_RIGHT));
    m_StartPosX = x;
    m_interval_end_temp = m_interval_end - deltaX;
    m_interval_begin_temp = m_interval_begin - deltaX;
        if ((m_interval_end == 0) | (m_interval_end_temp >= m_maxSize)) {
                    m_interval_begin_temp = m_interval_begin;
                    m_interval_end_temp = m_maxSize;
                }
                        else if (m_interval_end_temp <= 10) {                 //ограничение от выход за пределы значений массива данных
                                    m_interval_begin_temp = m_interval_begin;
                                    m_interval_end_temp = 9;
                                }
                                    else if (m_interval_begin_temp <= 1) {    //ограничение от выход за пределы значений массива данных
                                            m_interval_begin_temp = 0;
                                            m_interval_end_temp = m_interval_end;
                                            }
            PlotDraw(m_current_axes_name,m_interval_begin_temp, m_interval_end_temp, m_Y_offset_min, m_Y_offset_max);
   }

  if ((m_MotionOn == true) && (m_StartPosX < X_OFFSET_LEFT*width)) {    //сдвиг оси Y текущего графика
        deltaY = (y - m_StartPosY);
        m_StartPosY = y;
        if (deltaY > 0) deltaY = Y_SCROLL_COEF; else deltaY = -Y_SCROLL_COEF;
        m_Y_offset_max = m_Y_offset_max + deltaY;
        m_Y_offset_min = m_Y_offset_min - deltaY;
        PlotDraw(m_current_axes_name,m_interval_begin, m_interval_end, m_Y_offset_min, m_Y_offset_max);

  }

}
void Plot2D::AddDataToPlot(PlotData2D *data) {
    container::vector<PlotData2D*> ::iterator iter = find(m_plot_data, data);

   if (iter == m_plot_data.end()) {
        m_plot_data.push_back(data);
    }
     for (auto i = m_plot_data.begin(); i != m_plot_data.end(); i++)
        {
            m_PlotData2D_temp = *i;
            auto temp = m_PlotData2D_temp->GetSize();
                if (temp > m_maxSize) {
                    m_maxSize = temp;
                    }
        }
}

void Plot2D::OnDraw(const Cairo::RefPtr<Cairo::Context> &cr, uint16_t width, uint16_t height) {

    switch (OnDrawState)
    {
        case OnDrawSignals::DrawPlot:
        {
            PlotsDrawing(cr, width, height);
            AxesDrawing(cr, width, height);
            break;
        }
        case OnDrawSignals::DrawAxes:
        {


            break;
        }
        case OnDrawSignals::MovePLot:
        {

            break;
        }
         case OnDrawSignals::IdlePLot:
        {

            break;
        }

    }

}

void Plot2D::PlotsDrawing(const ::Cairo::RefPtr<::Cairo::Context> &cr,uint16_t width,uint16_t height)
{
    for (auto i = m_plot_data.begin(); i != m_plot_data.end(); i++)
        {
        m_PlotData2D_temp = *i;
            if (m_PlotData2D_temp->GetName() == m_current_axes_name) {
                    m_PlotData2D_temp->DataDraw(cr, width, height, m_interval_begin, m_interval_end,m_Y_offset_min, m_Y_offset_max);
                }
            else {
            m_PlotData2D_temp->DataDraw(cr, width, height, m_interval_begin, m_interval_end, 0.0, 0.0);
                }
    }
}

void Plot2D::PlotDraw(ustring axes_name,double interval_begin,double interval_end, double y_offset_min,double y_offset_max) //шкала Y берется от плота с именем axes_name
{
    OnDrawState = OnDrawSignals::DrawPlot; // выбирается функция перерисовки cairo графики+оси
    m_current_axes_name = axes_name;
    m_interval_begin = interval_begin;
    m_interval_end = interval_end;
    m_Y_offset_min = y_offset_min;
    m_Y_offset_max = y_offset_max;
    queue_draw();//полная перерисовка области
}

 void Plot2D::AxesDrawing(const ::Cairo::RefPtr<::Cairo::Context> &cr,uint16_t width,uint16_t height) //отрисовка осей и шкал
 {
    unsigned long long x_min;
    unsigned long long x_max;
    double y_min;
    double y_max;
    double stepX;
    double stepY;
    double stepX_data;
    double stepY_data;
    double step_temp;
    ustring  str;
    ustring  strY;
    Gdk::RGBA color;

        for (auto i = m_plot_data.begin(); i != m_plot_data.end(); i++)
            {
                m_PlotData2D_temp = *i;
                    if (m_PlotData2D_temp->GetName() == m_current_axes_name) {
                            y_min = m_PlotData2D_temp->GetMinY();
                            y_max = m_PlotData2D_temp->GetMaxY();
                            x_min = m_PlotData2D_temp->GetMinX();
                            x_max = m_PlotData2D_temp->GetMaxX();
                            color = m_PlotData2D_temp->GetColor();
                        }
            }

    stepX = (width - width * (X_OFFSET_LEFT + X_OFFSET_RIGHT)) / GRID_X;
    stepY = (height - height * (Y_OFFSET_UP + Y_OFFSET_DOWN)) / GRID_Y;
    stepY_data = (y_max - y_min) / GRID_Y;
    stepX_data = (x_max - x_min) / GRID_X;

    cr -> save();
    cr -> move_to(0, height);
    cr -> line_to(width, height); // рисуем ось Х(внизу холста)
    cr -> move_to(0, 0);
    cr -> line_to(0, height);// рисуем ось Y(слева холста)
    cr -> stroke();
    cr -> restore();

    cr -> save();
    cr -> set_source_rgba( color.get_red(), color.get_green(), color.get_blue(), color.get_alpha());

    step_temp = x_min;
    for (uint16_t i = 1 ; i <= GRID_X; i++)
        {
            cr -> move_to(X_OFFSET_LEFT * width + stepX * i, height - X_OFFSET_TEXT);
            step_temp = static_cast<unsigned long long>(step_temp + stepX_data);
            str = lexical_cast<std::string>(step_temp);
            cr -> show_text(str);
        }

    step_temp = y_min;
    for (uint16_t i = 1;i <= GRID_Y; i++)
        {
            cr -> move_to(Y_OFFSET_TEXT, height - (Y_OFFSET_UP * height + stepY * i));
            step_temp = step_temp + stepY_data;
            str = lexical_cast<std::string>(step_temp);
            uint16_t count{};
            for(const char c: str)
                {
                    count++;
                        if(c == ',') break;
                }
            std::string strY=str.substr(0,count+2); // обрезаем все, что после 2х знаков после запятой
            cr->show_text(strY);
        }
    cr -> stroke();
    cr -> restore();

}


